HTML 5 Game Development - Tutorial step 2
Ok, pretty lame, I know
Let's point to step2 game
import 'dart_02/game.dart'; void main() { //launch the game HTML5Game game = new HTML5Game(); game.startGame(); }
First, let's add a FPS counter. This will tell us how "smoothly" our game is rendering. This bit is taken from the Dart clock example, so I'll add nothing more
The current game is an infinite loop; the Player is "invincible". Let's print a health bar to display this information
/** * Draw character information. */ void draw(CanvasRenderingContext2D context) { //health int startPosX = 10; int startPosY = 10; int heigth = 20; int maxLength = 110; //calculate remaining health % int y = (health/100*(maxLength-startPosX)).round() ; //green - good context..fillStyle="green" ..fillRect(startPosX, startPosY, y , heigth); //red - bad context..fillStyle="red" ..fillRect(startPosX+y, startPosY, maxLength-y , heigth); super.draw(context); }
Then, let's catch the user input from the keyboard (sorry mobile user, it's just a small game). A classic WASD should do just fine; the user wants to escape, while the Monster will follow the Player around trying to kill him.
The Keyboard class is taken from here; I just want to point out something. It's another great use of the Future functionality (really use autocomplete and see what you can achieve with a couple of lines of code...it's just amazing)
// lastComands = new List(); // update() { //process keyboard event processKeyboardInput(); //etc } processKeyboardInput() { if (keyBoard.isPressed(KeyCode.W)) lastComands.add(Command.NORTH); if (keyBoard.isPressed(KeyCode.A)) lastComands.add(Command.WEST); if (keyBoard.isPressed(KeyCode.D)) lastComands.add(Command.EAST); if (keyBoard.isPressed(KeyCode.S)) lastComands.add(Command.SOUTH); }
Ok, so now each turn we collect the user input and create a list of Command to execute that will modify the world.
The Command class is just an enumaration of the possible four directions a user can select.
Google, really? Still no "real" enum in the language??
In a more advanced game obviously the possible action performed by the user would be much powerful
/** * Command to be executed. */ class Command { final _direction; const Command._internal(this._direction); toString() => 'Enum.$_direction'; static const NORTH = const Command._internal('NORTH'); static const SOUTH = const Command._internal('SOUTH'); static const WEST = const Command._internal('WEST'); static const EAST = const Command._internal('EAST'); }
Now that we have collected the actions we want to "apply" the user input to the Player. Let's construct a mixin (see this for more detail ); in two words, a mixin is an interface with a default implementation that might be shared in multiple class hierarchies
/** * Mixin for the characters to execute a Command. */ abstract class Commander { void applyCommands(Listl); }
So our mixin Commander will be applied to our Player so that he can still extends Character and benefit from the Commander implementation (empty in this case).
@override void applyCommands(Listl) { for(Command c in l) { if(c != null) { switch (c) { case Command.NORTH: y = y-3; break; case Command.SOUTH: y = y+3; break; case Command.EAST: x = x+3; break; case Command.WEST: x = x-3; break; } } } }
Ok, so now our Player will move around according to the user input. Summarizing in the update method, we have collected the user input and created a list of Command to apply to the Player. At the end of the method, we just need to clear the list for the next loop.
update() { //process keyboard event processKeyboardInput(); //hurt a little the hero p.decrementHealth(); //move randmly the monster m.x = r.nextInt(WIDTH); m.y = r.nextInt(HEIGHT); //player move to escape p.applyCommands(lastComands); //clean player command list for the next loop lastComands.clear(); }
Ok, let's try it out.
It doesn't look "real" with the monster moving around like crazy, let's fix that in step3